#pragma once
#include <thread>
#include <functional>
#include <mutex>
#include <iostream>
using namespace std;

template<class T>
class ThreadPool
{
    #define THREAD_SIZE 5
    using func_t = function<void(const int&)>;
public:
    static ThreadPool<T>* GetInstance(const func_t& func = [](const T& val){cout << "消费者消费了一条数据" << endl;})
    {
        if(_inst == nullptr)
        {
            {
                unique_lock<mutex> lck(_mutex);
                if(_inst == nullptr)
                {
                    new ThreadPool<T>(func);
                }
            }
            _inst->Init();
            _inst->Start();
        }
        _inst->_func = func;
        return _inst;
    }
private:
    ThreadPool(func_t func, size_t size = THREAD_SIZE):_size(size), _func(func)
    {}
    ~ThreadPool()
    {
        for(size_t i = 0; i < _size; i++)
        {
            threads[i].join();
        }
    }

    void Init()
    {}

    void Start()
    {
        for(int i = 0; i < _size; i++)
        {
            threads.push_back(thread(Routine, this));
        }
    }

    static void Routine(ThreadPool<int>* ptr)
    {
        while(true)
        {
            T t;
            ptr->bq.Pop(&t);
            ptr->_func(t);
        }
    }

public:
    void PushTask(const T& task)
    {
        bq.Push(task);
    }
private:
    BlockQueue<T> bq;
    vector<thread> threads;
    size_t _size;
    func_t _func;
    static mutex _mutex;
    static ThreadPool<T>* _inst;
};

template<class T>
ThreadPool<T>* ThreadPool<T>::_inst = nullptr;